
/**
\file
Defines the rainbow::stencil_state class.
\author Sean Tromans
*/

#include <sun_pre_compiled.hpp>
#include <rainbow/graphics_device/stencil_state/stencil_state.hpp>
#include <numeric>

#if !defined(SUN_DISABLE_LUA)
#include <sun/lua/lua.hpp>
#endif

namespace rainbow
{

	/**
	Default Constructor.
	\author Sean Tromans
	*/
	stencil_state::stencil_state( )
		:		enabled(false)

			,	front_func(GL_ALWAYS)
			,	front_ref(0)
			,	front_func_mask( std::numeric_limits<GLuint>::max( ) )

			,	back_func(GL_ALWAYS)
			,	back_ref(0)
			,	back_func_mask( std::numeric_limits<GLuint>::max( ) )

			,	front_stencil_fail(GL_KEEP)
			,	front_depth_fail(GL_KEEP)
			,	front_both_pass(GL_KEEP)

			,	back_stencil_fail(GL_KEEP)
			,	back_depth_fail(GL_KEEP)
			,	back_both_pass(GL_KEEP)

			,	front_mask( std::numeric_limits<GLuint>::max( ) )
			,	back_mask( std::numeric_limits<GLuint>::max( ) )
	{}

	/**
	Applies any changes from the provided stencil_state to OpenGL.
	@param other The currently active stencil_state.
	\author Sean Tromans
	*/
	void stencil_state::apply( stencil_state const & other ) const
	{
		if ( enabled != other.enabled )
		{
			sun::detail::set_state( GL_STENCIL_TEST, enabled );
		}

		if (	front_func != other.front_func
			||	front_ref != other.front_ref
			||	front_func_mask != other.front_func_mask )
		{
			glStencilFuncSeparate( GL_FRONT, front_func, front_ref, front_func_mask );
		}

		if (	back_func != other.back_func
			||	back_ref != other.back_ref
			||	back_func_mask != other.back_func_mask )
		{
			glStencilFuncSeparate( GL_BACK, back_func, back_ref, back_func_mask );
		}

		if (	front_stencil_fail != other.front_stencil_fail
			||	front_depth_fail != other.front_depth_fail
			||	front_both_pass != other.front_both_pass )
		{
			glStencilOpSeparate( GL_FRONT, front_stencil_fail, front_depth_fail, front_both_pass );
		}

		if (	back_stencil_fail != other.back_stencil_fail
			||	back_depth_fail != other.back_depth_fail
			||	back_both_pass != other.back_both_pass )
		{
			glStencilOpSeparate( GL_BACK, back_stencil_fail, back_depth_fail, back_both_pass );
		}

		if ( front_mask != other.front_mask )
		{
			glStencilMaskSeparate( GL_FRONT, front_mask );
		}

		if ( back_mask != other.back_mask )
		{
			glStencilMaskSeparate( GL_BACK, front_mask );
		}
	}

	/**
	Exposes the stencil_state to lua.
	@param L The Lua State being initialised.
	\author Sean Tromans.
	*/
	void stencil_state::lua_interface( lua_State * L )
	{
#if !defined(SUN_DISABLE_LUA)
		luabind::module( L, "rainbow" )
		[
			luabind::class_< stencil_state >( "stencil_state" )
				.def( luabind::constructor<>( ) )
				.def_readwrite( "enabled", &stencil_state::enabled )

				.def_readwrite( "front_func", &stencil_state::front_func )
				.def_readwrite( "front_ref", &stencil_state::front_ref )
				.def_readwrite( "front_func_mask", &stencil_state::front_func_mask )

				.def_readwrite( "back_func", &stencil_state::back_func )
				.def_readwrite( "back_ref", &stencil_state::back_ref )
				.def_readwrite( "back_func_mask", &stencil_state::back_func_mask )

				.def_readwrite( "front_stencil_fail", &stencil_state::front_stencil_fail )
				.def_readwrite( "front_depth_fail", &stencil_state::front_depth_fail )
				.def_readwrite( "front_both_pass", &stencil_state::front_both_pass )

				.def_readwrite( "back_stencil_fail", &stencil_state::back_stencil_fail )
				.def_readwrite( "back_depth_fail", &stencil_state::back_depth_fail )
				.def_readwrite( "back_both_pass", &stencil_state::back_both_pass )

				.def_readwrite( "front_mask", &stencil_state::front_mask )
				.def_readwrite( "back_mask", &stencil_state::back_mask )
				.def( luabind::const_self == luabind::const_self )
		];
#else
		NMN_UNUSED( L );
#endif
	}

	/**
	Comparrison operator for stencil_states.
	@param lhs The first stencil_state being compared.
	@param rhs The second stencil_state being compared.
	@return True if the stencil_states are equal.
	\author Sean Tromans
	*/
	bool operator == ( stencil_state const & lhs, stencil_state const & rhs )
	{
		return	lhs.enabled == rhs.enabled
			&&	lhs.front_func == rhs.front_func
			&&	lhs.front_ref == rhs.front_ref
			&&	lhs.front_func_mask == rhs.front_func_mask
			&&	lhs.back_func == rhs.back_func
			&&	lhs.back_ref == rhs.back_ref
			&&	lhs.back_func_mask == rhs.back_func_mask
			&&	lhs.front_stencil_fail == rhs.front_stencil_fail
			&&	lhs.front_depth_fail == rhs.front_depth_fail
			&&	lhs.front_both_pass == rhs.front_both_pass
			&&	lhs.back_stencil_fail == rhs.back_stencil_fail
			&&	lhs.back_depth_fail == rhs.back_depth_fail
			&&	lhs.back_both_pass == rhs.back_both_pass
			&&	lhs.front_mask == rhs.front_mask
			&&	lhs.back_mask == rhs.back_mask;
	}

}
